/***********************************************************************
 created:	14/3/2004
 author:		Paul D Turner

 purpose:	Defines interface for Size class
 *************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifndef _CEGUISize_h_
#define _CEGUISize_h_

#include <typeinfo>
#include <ostream>

/*!
 \brief
 How aspect ratio should be maintained
 */
enum AspectMode {
	//! Ignores the target aspect (default)
	AM_IGNORE,
	/*!
	 Satisfies the aspect ratio by shrinking the size as little
	 as possible to fit inside it
	 */
	AM_SHRINK,
	/*!
	 Satisfies the aspect ratio by expanding the widget as little
	 as possible outside it
	 */
	AM_EXPAND
};

/*!
 * \brief allows you to get UDim::relative() if you pass UDim or just 1 if you pass anything else
 *
 * \todo Is this the right place where to put this?
 */
template<typename T>
inline T TypeSensitiveOne() {
	return T(1);
}
/*!
 * \brief allows you to get UDim(0, 0) if you pass UDim or just 0 if you pass anything else
 *
 * \todo Is this the right place where to put this?
 */
template<typename T>
inline T TypeSensitiveZero() {
	return T(0);
}
/*!
 \brief
 Class that holds the size (width & height) of something.
 */
template<typename T>
class Size {
public:
	typedef T value_type;

	inline Size() {
	}

	inline Size(const T width, const T height) :
			d_width(width), d_height(height) {
	}

	inline Size(const Size& v) :
			d_width(v.d_width), d_height(v.d_height) {
	}

	inline bool operator==(const Size& other) const {
		return d_width == other.d_width && d_height == other.d_height;
	}

	inline bool operator!=(const Size& other) const {
		return !operator==(other);
	}

	inline Size operator*(const T c) const {
		return Size(d_width * c, d_height * c);
	}

	inline Size operator*(const Size& s) const {
		return Size(d_width * s.d_width, d_height * s.d_height);
	}

	inline Size operator+(const Size& s) const {
		return Size(d_width + s.d_width, d_height + s.d_height);
	}

	inline Size operator-(const Size& s) const {
		return Size(d_width - s.d_width, d_height - s.d_height);
	}

	inline void clamp(const Size& min, const Size& max) {
		assert(min.d_width <= max.d_width);
		assert(min.d_height <= max.d_height);

		if (d_width < min.d_width)
			d_width = min.d_width;
		else if (d_width > max.d_width)
			d_width = max.d_width;

		if (d_height < min.d_height)
			d_height = min.d_height;
		else if (d_height > max.d_height)
			d_height = max.d_height;
	}

	inline void scaleToAspect(AspectMode mode, T ratio) {
		if (mode == AM_IGNORE)
			return;

		if (d_width <= 0 && d_height <= 0)
			return;

		assert(ratio > 0);

		const T expectedWidth = d_height * ratio;
		const bool keepHeight =
				(mode == AM_SHRINK) ?
						expectedWidth <= d_width : expectedWidth >= d_width;

		if (keepHeight) {
			d_width = expectedWidth;
		} else {
			d_height = d_width / ratio;
		}
	}

	/*!
	 \brief allows writing the size to std ostream
	 */
	inline friend std::ostream& operator <<(std::ostream& s, const Size& v) {
		s << "CEGUI::Size<" << typeid(T).name() << ">(" << v.d_width << ", "
				<< v.d_height << ")";
		return s;
	}

	//! \brief finger saving alias for Size(side, side)
	inline static Size square(const T side) {
		return Size(side, side);
	}

	//! \brief finger saving alias for Size(0, 0)
	inline static Size zero() {
		return square(TypeSensitiveZero<T>());
	}

	//! \brief finger saving alias for Size(1, 1)
	inline static Size one() {
		return square(TypeSensitiveOne<T>());
	}

	//! \brief finger saving alias for Size(1, 0)
	inline static Size one_width() {
		return Size(TypeSensitiveOne<T>(), TypeSensitiveZero<T>());
	}

	//! \brief finger saving alias for Size(0, 1)
	inline static Size one_height() {
		return Size(TypeSensitiveOne<T>(), TypeSensitiveZero<T>());
	}

	T d_width;
	T d_height;
};

// the main reason for this is to keep C++ API in sync with other languages
typedef Size<float> Sizef;
//typedef Size<UDim> USize;

//inline USize operator*(const USize& i, float x) {
//	return i * UDim(x, x);
//}

#endif	// end of guard _CEGUISize_h_
